#include "System.h"
#include "Mailer.h"
#include "Node.h"
#include "RequestClockMessage.h"
#include "SetOwnClockMessage.h"
#include "SyncMessage.h"

System::System(void)
{
	m_scheduler = new Scheduler();
	m_ready = false;
	m_numOfSteps = 0;
}

System::~System(void)
{
	delete m_scheduler;
}

void System::config(const char *fileName)
{
	m_config = new Configuration(fileName);
	m_mailer = new Mailer(this, m_config->getMailerBufferSize());
	m_nodes = new NODES;
	m_scheduler->setMailer(m_mailer);
	changePolicy(m_config->getSchedulingPolicy());

	int nodes = m_config->getNumOfNodes();
	int** nodeGraph = m_config->getNodeGraph();
	int i;
	//create nodes
	for(i=0;i<nodes;i++)
	{
		Node* n = new Node(i+1, this, m_mailer, nodeGraph[i][0]); 
		m_nodes->insert(NODES::value_type(i+1, n));
	}

	//add neighbours
	for(i=0;i<nodes;i++)
	{
		Node* n = m_nodes->find(i+1)->second;
		for(int j=0;j<nodes;j++)
		{
			if(nodeGraph[i][j+1])
			{
				n->addNeighbour(getThreadID(j+1));
			}
		}
	}
	startTimer();
	m_scheduler->startAll();
	
	//send sync messages to all nodes
	NODES::iterator it;
	for(it=m_nodes->begin();it!=m_nodes->end();it++)
	{
		SendSyncMsg(0,it->second->getThreadID());
	}
	
	m_ready = true;
}

/*
void System::killAll()
{
	//exit all Node's Threads
	NODES::iterator it;
	for(it=m_nodes->begin();it!=m_nodes->end();)
	{
		NODES::iterator tmp = it++;
		delete tmp->second;
		m_nodes->erase(tmp->first);
	}
	m_mailer->stop();
	delete m_mailer;
	delete m_nodes;
	m_scheduler->killCrashed();
}
*/

void System::killAll()
{
//exit all Node's Threads
	m_scheduler->KillAll();
	NODES::iterator it = m_nodes->begin();
	while(it!=m_nodes->end())
	{
//		m_scheduler->m_scheduables->erase(it->first);
		NODES::iterator tmp = it++;
		delete tmp->second;
		m_nodes->erase(tmp->first);
	}
//	m_scheduler->m_scheduables->clear();
	m_mailer->stop();
	delete m_mailer;
	m_mailer = NULL;
	delete m_nodes;
	m_nodes = NULL;
	delete m_config;
	m_config = NULL;
	m_scheduler->killCrashed();
}


int System::SendSyncMsg(int source, int nodeId, int counter)
{
	SyncMessage* msg = new SyncMessage(source,nodeId, m_nodes->size(), nodeId);
	NODES::iterator it;
	for(it=m_nodes->begin();it!=m_nodes->end();it++)
	{
		msg->m_flags->insert(map<int,int>::value_type(it->second->getThreadID(), 0));
	}
	msg->m_timeSteps = counter;
	return m_mailer->send(nodeId,msg);
}

int System::getNodeStatus(int nodeId)
{
	return m_scheduler->getNodeStatus(nodeId);
}

void System::addShellsMailbox(int id)
{
	m_mailer->addMailbox(id);
}

void System::start()
{
	m_scheduler->start();
}

void System::stop()
{
	m_scheduler->stop();
}

/*
Thread* System::createThread(void* (*func)(void*), void* arg)
{
	Thread* t = new Thread(func, arg);
	if(m_scheduler) m_scheduler->addThread(t);
	if(m_mailer) m_mailer->addMailbox(t->getID());
	return t;
}
*/

Thread* System::createThread(void* (*func)(void*), void* arg)
{
	Thread* t = new Thread(func, arg);
	iSchedulable* sched = (iSchedulable*)arg;
	if(m_scheduler) m_scheduler->addThread(t, sched);
	if(m_mailer) m_mailer->addMailbox(t->getID());
	return t;
}

void System::startAll()
{
	m_scheduler->startAll();
}

Message* System::recieve(int id)
{
	return m_mailer->recieve(id);
}

void System::changePolicy(int policy)
{
	m_scheduler->changePolicy(policy);
}

int System::getThreadID(int nodeID)
{
	return m_nodes->find(nodeID)->second->getThreadID();
}

int System::crashNode(int id)
{
	return m_scheduler->crashNode(getThreadID(id));
}

int System::reviveNode(int id)
{
	int res = m_scheduler->reviveNode(getThreadID(id));
	if(res == 1)
	{
		SyncMessage* msg = new SyncMessage(0,getThreadID(id),m_nodes->size(), getThreadID(id));
		m_mailer->send(getThreadID(id),msg);
	}
	return res;
}

void System::setNodeClock(int id, int newClock)
{
	int dest = getThreadID(id);
	Message* m = new SetOwnClockMessage(0, dest, newClock);
	m_mailer->send(dest, m);
}

void System::sendNodeClock(int id)
{
	int dest = getThreadID(id);
	Message* m = new RequestClockMessage(0, dest);
	m_mailer->send(dest, m);
}

int System::getTotalTime()
{
	return m_scheduler->getTotalCPU();
}

int System::sendNodeTime(int id)
{
	return m_scheduler->getNodeCPU(getThreadID(id));
}

int System::getMaxLength()
{
	return m_mailer->getMaxLength();
}

int System::getInboxMaxLength(int id)
{
	return m_mailer->getInboxMaxLength(getThreadID(id));
}

int System::getMailboxSize(int id)
{
	return m_mailer->getMailboxSize(id);
}

int System::getNumOfSteps()
{
	return m_numOfSteps;
}

int System::getNumOfNodes()
{
	return m_nodes->size();
}

void System::startTimer()
{
	m_scheduler->startCounting();
	m_mailer->startCounting();
}

void System::stopTimer()
{
	m_scheduler->stopCounting();
	m_mailer->stopCounting();
}
